package com.example.vm.schedul;

import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.common.constant.AuditStatus;
import com.common.constant.RedisConstant;
import com.example.vm.domain.entity.Video;
import com.example.vm.domain.vo.HotVideo;
import com.example.vm.domain.vo.VideoVO;
import com.example.vm.service.VideoService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 定时类，定期更新视频热门榜
 */
@Component
public class HotRank {
    @Autowired
    private VideoService videoService;
    @Autowired
    private RedisTemplate redisTemplate;

    Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer<>(Object.class);
    
    ObjectMapper objectMapper = new ObjectMapper();
    {
        serializer.setObjectMapper(objectMapper);
    }
    //每一个小时更新一次,-----热度排行榜
    @Scheduled(cron = "0 0 */1 * * ?")
//    @Scheduled(fixedRate = 60000)
    public void VideoHotRank(){
        System.out.println("更新了热度排行榜");
        //控制数量
        TopK topK=new TopK(10, new PriorityQueue<>(10, Comparator.comparing(HotVideo::getHot)));
        int limit = 1000;
            
        //获取规定的热度最低标准
//        double hotLimit = 0.88;
        //获取当前日期
//        Calendar calendar = Calendar.getInstance();
//        int days = calendar.get(Calendar.DATE);
        //每次拿一千条数据
        long id = 0;
        List<Video> videos = videoService.list(new LambdaQueryWrapper<Video>().select(Video::getId, 
                Video::getShareNum, Video::getHistoryNum, Video::getLikeNum, Video::getCollectNum,
                Video::getCreatedTime, Video::getTitle)
                .gt(Video::getId, id)
                .eq(Video::getAuditStatus, AuditStatus.SUCCESS)
                .eq(Video::getOpen,0)
                .last("limit "+limit));
//        System.out.println("videos:::"+videos);
        while(!ObjectUtils.isEmpty(videos)){
//            List<Long> hotVideos = new ArrayList<>();
            for(Video video:videos){
                System.out.println("video::::"+video);
                double collectNum = video.getCollectNum()*1.5;
                double historyNum = video.getHistoryNum()*0.8;
                double likeNum = video.getLikeNum()*1.2;
                double shareNum = video.getShareNum()*1.3;
                Date date = new Date();
                long l = date.getTime() - video.getCreatedTime().getTime();
                //获取随机小数，用于去重
                double w = weightRandom();
                System.out.println("权重="+(collectNum+historyNum+likeNum+shareNum));
                System.out.println("l=="+l);
                double hot = hot(collectNum + historyNum + likeNum + shareNum+w, TimeUnit.MILLISECONDS.toDays(l));
                System.out.println("hot:::"+hot);
                topK.add(new HotVideo(hot, video.getId(), video.getTitle()));
            }
            id=videos.get(videos.size()-1).getId();
            videos=videoService.selectNDaysVideo(id,3,limit);
            
        }
        List<HotVideo> hotVideoList = topK.get();
        
        System.out.println("HostList:::::::"+hotVideoList);
        
        byte[] keys = RedisConstant.HOT_RANK.getBytes();
        Double minHot = hotVideoList.get(0).getHot();
        redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
            for (HotVideo hotVideo : hotVideoList) {
                double score = hotVideo.getHot();
                try {
                    // 使用 redisTemplate 的 valueSerializer 进行序列化
                    byte[] serializedHotVideo = redisTemplate.getValueSerializer().serialize(objectMapper.writeValueAsBytes(hotVideo));
                    connection.zAdd(keys, score, serializedHotVideo);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            redisTemplate.opsForZSet().removeRangeByScore(RedisConstant.HOT_RANK, minHot,0);
            return null;
        });
    }

    public double weightRandom() {
        int i = (int) ((Math.random() * 9 + 1) * 100000);
        return i / 1000000.0;
    }

    //热门视频，生成后存redis里，用户获取速度快，一天一更
        @Scheduled(fixedRate = 60000)
    public void VideoHot(){
        //控制数量
        int limit = 1000;
        long id = 1L;
        //分片查询三天内视频，限制为一千条
        List<Video> videos = videoService.selectNDaysVideo(id, 3, limit);
        //设定热门视频最低热度
        double hotLimit = 0.5;
        
        //获取当日日期
        Calendar calendar = Calendar.getInstance();
        int days = calendar.get(Calendar.DATE);

        Set<Long> hotVideos = new HashSet<>();
        while (!ObjectUtils.isEmpty(videos)) {
            for(Video video:videos){
                double collectNum = video.getCollectNum()*1.5;
                double historyNum = video.getHistoryNum()*0.8;
                double likeNum = video.getLikeNum()*1.2;
                double shareNum = video.getShareNum()*1.3;
                
                //生成随机六位小数去重
                double w = weightRandom();
                
                Date date = new Date();
                long l = date.getTime() - video.getCreatedTime().getTime();
                double hot = hot(collectNum + historyNum + likeNum + shareNum+w, TimeUnit.MILLISECONDS.toDays(l));
                if(hot>hotLimit){
                    hotVideos.add(video.getId());
                }
            }
            id=videos.get(videos.size()-1).getId();
            videos=videoService.selectNDaysVideo(id,3,limit);
        }
        if(!ObjectUtils.isEmpty(hotVideos)){
            String key = RedisConstant.HOT_VIDEO+days;
            redisTemplate.opsForSet().add(key, hotVideos);
            redisTemplate.expire(key, 24, TimeUnit.HOURS);
        }
    }
    
    
    
    
    //半衰期公式，用于计算热度
    static double a = 0.011;
    public static double hot(double weight, double t) {
        return weight * Math.exp(-a * t);
    }

}
